package com.liaoyifan.core.aspect;

import com.liaoyifan.core.util.Json;
import com.liaoyifan.core.util.RequestContext;
import com.liaoyifan.core.util.Token;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;

@SuppressWarnings("unused")
@Aspect
@Order(1)
public class WebAspect {

    public static final long SLOW = 2000;
    private static final String HEADER_FORMAT = "{} -> Header: {}";
    private static final String PARAMS_FORMAT = "{} -> Params: {}, Subject: {}";
    private static final String RETURN_FORMAT = "{} -> Return: {}, Subject: {}, Duration: {}ms";
    private static final String SLOW_RETURN_FORMAT = "[SLOW] " + RETURN_FORMAT;
    private static final Logger log = LogManager.getLogger(WebAspect.class.getSimpleName());

    @SuppressWarnings("EmptyMethod")
    @Pointcut(
            "(@annotation(org.springframework.web.bind.annotation.RequestMapping) "
                    + "|| @annotation(org.springframework.web.bind.annotation.PostMapping) "
                    + "|| @annotation(org.springframework.web.bind.annotation.GetMapping)) "
                    + "&& !within(@org.springframework.cloud.openfeign.FeignClient *)")
    public void logAspectPointcut() {}

    @Before(value = "logAspectPointcut()")
    public void logAspectBefore(JoinPoint joinPoint) {
        HttpServletRequest request = RequestContext.getRequest();
        String servletPath = request.getServletPath();
        log.info(HEADER_FORMAT, servletPath, RequestContext.getKeyHeaders(request));
        log.info(
                PARAMS_FORMAT,
                servletPath,
                RequestContext.getMethodArgs(joinPoint.getArgs()),
                Token.subject(request));
    }

    @Around("logAspectPointcut()")
    public Object logAspectAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - startTime;
        HttpServletRequest request = RequestContext.getRequest();
        log.info(
                duration > SLOW ? SLOW_RETURN_FORMAT : RETURN_FORMAT,
                request.getServletPath(),
                Json.toString(result),
                Token.subject(request),
                duration);
        return result;
    }
}
